module Exp_Compress (
    input clk_i,
    input rst_n_i,

    input [511:0] msg_padding_in,
    input block_start_in,
    input [255:0] V_in,

    output [255:0] Vi_out,
    output block_comp_finished_out 
);

wire W_valid;
wire [31:0] W_j0, W_j1, W_j2;
wire [31:0] W_j_p0, W_j_p1, W_j_p2;
reg [31:0] A_in_0;
reg [31:0] B_in_0;
reg [31:0] C_in_0;
reg [31:0] D_in_0;
reg [31:0] E_in_0;
reg [31:0] F_in_0;
reg [31:0] G_in_0;
reg [31:0] H_in_0;
wire [31:0] A_out_0;
wire [31:0] B_out_0;
wire [31:0] C_out_0;
wire [31:0] D_out_0;
wire [31:0] E_out_0;
wire [31:0] F_out_0;
wire [31:0] G_out_0;
wire [31:0] H_out_0;
wire [31:0] A_out_1;
wire [31:0] B_out_1;
wire [31:0] C_out_1;
wire [31:0] D_out_1;
wire [31:0] E_out_1;
wire [31:0] F_out_1;
wire [31:0] G_out_1;
wire [31:0] H_out_1;
wire [31:0] A_out_2;
wire [31:0] B_out_2;
wire [31:0] C_out_2;
wire [31:0] D_out_2;
wire [31:0] E_out_2;
wire [31:0] F_out_2;
wire [31:0] G_out_2;
wire [31:0] H_out_2;

//Wj and Wj' register------------------------------------
reg [31:0] W_j0_r;
reg [31:0] W_j1_r;
reg [31:0] W_j2_r;
reg [31:0] W_j_p0_r;
reg [31:0] W_j_p1_r;
reg [31:0] W_j_p2_r;

always @(posedge clk_i) begin
    if(!rst_n_i) begin
        W_j0_r <= 32'b0;
        W_j1_r <= 32'b0;
        W_j2_r <= 32'b0;
        W_j_p0_r <= 32'b0;
        W_j_p1_r <= 32'b0;
        W_j_p2_r <= 32'b0;
    end
    else if(W_valid) begin
        W_j0_r <= W_j0;
        W_j1_r <= W_j1;
        W_j2_r <= W_j2;
        W_j_p0_r <= W_j_p0;
        W_j_p1_r <= W_j_p1;
        W_j_p2_r <= W_j_p2;
    end
end

//index counter---------------------------------------------
reg [5:0] j_cnt_0;   //compress function j iter from 0 to 63 for a block
wire [5:0] j_cnt_1;
wire [5:0] j_cnt_2;

reg W_valid_r;
wire counter_reset;
wire W_valid_redge;

always @(posedge clk_i) begin
   if(counter_reset)
        j_cnt_0 <= 6'b0;
    else if(W_valid)
        j_cnt_0 <= j_cnt_0 + 3;
end

always @(posedge clk_i) begin
    if(!rst_n_i)
        W_valid_r <= 1'b0;
    else
        W_valid_r <= W_valid;
end

assign j_cnt_1 = j_cnt_0 + 1;
assign j_cnt_2 = j_cnt_0 + 2;
assign W_valid_redge = (W_valid & ~W_valid_r);
assign counter_reset = (~rst_n_i) | W_valid_redge | block_start_in;


//A0~H0 put into CF0----------------------------------------
always @(posedge clk_i) begin
    if(!rst_n_i) begin
        A_in_0 <= 32'b0;
        B_in_0 <= 32'b0;
        C_in_0 <= 32'b0;
        D_in_0 <= 32'b0;
        E_in_0 <= 32'b0;
        F_in_0 <= 32'b0;
        G_in_0 <= 32'b0;
        H_in_0 <= 32'b0;
    end
    else if(W_valid_redge) begin
        A_in_0 <= V_in[255-:32];
        B_in_0 <= V_in[223-:32];
        C_in_0 <= V_in[191-:32];
        D_in_0 <= V_in[159-:32];
        E_in_0 <= V_in[127-:32];
        F_in_0 <= V_in[95-:32];
        G_in_0 <= V_in[63-:32];
        H_in_0 <= V_in[31-:32];
    end
    else if(W_valid) begin
        A_in_0 <= A_out_2;
        B_in_0 <= B_out_2;
        C_in_0 <= C_out_2;
        D_in_0 <= D_out_2;
        E_in_0 <= E_out_2;
        F_in_0 <= F_out_2;
        G_in_0 <= G_out_2;
        H_in_0 <= H_out_2;
    end
end

//output---------------------------------------------------------------------
assign Vi_out = {A_out_0, B_out_0, C_out_0, D_out_0, E_out_0, F_out_0, G_out_0, H_out_0};
assign block_comp_finished_out = (j_cnt_0 == 63) & (!block_start_in);



//module connection----------------------------------------------------------

MSG_EXP MSG_EXP_0(
    .clk_i(clk_i),
    .rst_n_i(rst_n_i),

    .msg_block_i(msg_padding_in),
    .block_start_i(block_start_in),

    .W_valid(W_valid),
    .last_Wp_group(),
    .W_j0(W_j0),
    .W_j1(W_j1),
    .W_j2(W_j2),
    .W_j_p0(W_j_p0),
    .W_j_p1(W_j_p1),
    .W_j_p2(W_j_p2)
);


Compress_Func CF_0(
    .index_j_in(j_cnt_0),
    .A_in(A_in_0),
    .B_in(B_in_0),
    .C_in(C_in_0),
    .D_in(D_in_0),
    .E_in(E_in_0),
    .F_in(F_in_0),
    .G_in(G_in_0),
    .H_in(H_in_0),
    .Wj_in(W_j0_r),
    .Wj_p_in(W_j_p0_r),

    .A_out(A_out_0),
    .B_out(B_out_0),
    .C_out(C_out_0),
    .D_out(D_out_0),
    .E_out(E_out_0),
    .F_out(F_out_0),
    .G_out(G_out_0),
    .H_out(H_out_0)
);

Compress_Func CF_1(
    .index_j_in(j_cnt_1),
    .A_in(A_out_0),
    .B_in(B_out_0),
    .C_in(C_out_0),
    .D_in(D_out_0),
    .E_in(E_out_0),
    .F_in(F_out_0),
    .G_in(G_out_0),
    .H_in(H_out_0),
    .Wj_in(W_j1_r),
    .Wj_p_in(W_j_p1_r),

    .A_out(A_out_1),
    .B_out(B_out_1),
    .C_out(C_out_1),
    .D_out(D_out_1),
    .E_out(E_out_1),
    .F_out(F_out_1),
    .G_out(G_out_1),
    .H_out(H_out_1)
);

Compress_Func CF_2(
    .index_j_in(j_cnt_2),
    .A_in(A_out_1),
    .B_in(B_out_1),
    .C_in(C_out_1),
    .D_in(D_out_1),
    .E_in(E_out_1),
    .F_in(F_out_1),
    .G_in(G_out_1),
    .H_in(H_out_1),
    .Wj_in(W_j2_r),
    .Wj_p_in(W_j_p2_r),

    .A_out(A_out_2),
    .B_out(B_out_2),
    .C_out(C_out_2),
    .D_out(D_out_2),
    .E_out(E_out_2),
    .F_out(F_out_2),
    .G_out(G_out_2),
    .H_out(H_out_2)
);

endmodule //Exp_Compress